package net.sourceforge.jaad.aac.error;

import net.sourceforge.jaad.aac.AACException;
import net.sourceforge.jaad.aac.syntax.IBitStream;

/**
 * This class is part of JAAD ( jaadec.sourceforge.net ) that is distributed
 * under the Public Domain license. Code changes provided by the JCodec project
 * are distributed under FreeBSD license.
 * 
 * @author in-somnia
 */
public class BitsBuffer {

	int bufa, bufb, len;
    //bit-twiddling helpers
    static final int[] S = {1, 2, 4, 8, 16};
    static final int[] B = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF};

	public BitsBuffer() {
		len = 0;
	}

	public int getLength() {
		return len;
	}

	public int showBits(int bits) {
		if(bits==0) return 0;
		if(len<=32) {
			//huffman_spectral_data_2 needs to read more than may be available,
			//bits maybe > len, deliver 0 than
			if(len>=bits) return ((bufa>>(len-bits))&(0xFFFFFFFF>>(32-bits)));
			else return ((bufa<<(bits-len))&(0xFFFFFFFF>>(32-bits)));
		}
		else {
			if((len-bits)<32) return ((bufb&(0xFFFFFFFF>>(64-len)))<<(bits-len+32))|(bufa>>(len-bits));
			else return ((bufb>>(len-bits-32))&(0xFFFFFFFF>>(32-bits)));
		}
	}

	public boolean flushBits(int bits) {
		len -= bits;

		boolean b;
		if(len<0) {
			len = 0;
			b = false;
		}
		else b = true;
		return b;
	}

	public int getBits(int n) {
		int i = showBits(n);
		if(!flushBits(n)) i = -1;
		return i;
	}

	public int getBit() {
		int i = showBits(1);
		if(!flushBits(1)) i = -1;
		return i;
	}

	public void rewindReverse() {
		if(len==0) return;
		final int[] i = BitsBuffer.rewindReverse64(bufb, bufa, len);
		bufb = i[0];
		bufa = i[1];
	}

	//merge bits of a to b
	public void concatBits(BitsBuffer a) {
		if(a.len==0) return;
		int al = a.bufa;
		int ah = a.bufb;

		int bl, bh;
		if(len>32) {
			//mask off superfluous high b bits
			bl = bufa;
			bh = bufb&((1<<(len-32))-1);
			//left shift a len bits
			ah = al<<(len-32);
			al = 0;
		}
		else {
			bl = bufa&((1<<(len))-1);
			bh = 0;
			ah = (ah<<(len))|(al>>(32-len));
			al = al<<len;
		}

		//merge
		bufa = bl|al;
		bufb = bh|ah;

		len += a.len;
	}

	public void readSegment(int segwidth, IBitStream _in) throws AACException {
		len = segwidth;

		if(segwidth>32) {
			bufb = _in.readBits(segwidth-32);
			bufa = _in.readBits(32);
		}
		else {
			bufa = _in.readBits(segwidth);
			bufb = 0;
		}
	}

    //32 bit rewind and reverse
    static int rewindReverse32(int v, int len) {
        v = ((v>>S[0])&B[0])|((v<<S[0])&~B[0]);
        v = ((v>>S[1])&B[1])|((v<<S[1])&~B[1]);
        v = ((v>>S[2])&B[2])|((v<<S[2])&~B[2]);
        v = ((v>>S[3])&B[3])|((v<<S[3])&~B[3]);
        v = ((v>>S[4])&B[4])|((v<<S[4])&~B[4]);
    
        //shift off low bits
        v >>= (32-len);
    
        return v;
    }
    
    //64 bit rewind and reverse
    static int[] rewindReverse64(int hi, int lo, int len) {
    	int[] i = new int[2];
    	if(len<=32) {
    		i[0] = 0;
    		i[1] = rewindReverse32(lo, len);
    	}
    	else {
    		lo = ((lo>>S[0])&B[0])|((lo<<S[0])&~B[0]);
    		hi = ((hi>>S[0])&B[0])|((hi<<S[0])&~B[0]);
    		lo = ((lo>>S[1])&B[1])|((lo<<S[1])&~B[1]);
    		hi = ((hi>>S[1])&B[1])|((hi<<S[1])&~B[1]);
    		lo = ((lo>>S[2])&B[2])|((lo<<S[2])&~B[2]);
    		hi = ((hi>>S[2])&B[2])|((hi<<S[2])&~B[2]);
    		lo = ((lo>>S[3])&B[3])|((lo<<S[3])&~B[3]);
    		hi = ((hi>>S[3])&B[3])|((hi<<S[3])&~B[3]);
    		lo = ((lo>>S[4])&B[4])|((lo<<S[4])&~B[4]);
    		hi = ((hi>>S[4])&B[4])|((hi<<S[4])&~B[4]);
    
    		//shift off low bits
    		i[1] = (hi>>(64-len))|(lo<<(len-32));
    		i[1] = lo>>(64-len);
    	}
    	return i;
    }

}
